package lexer

import (
	"interpreter/token"
	"strings"
	"unicode"
	"unicode/utf8"
)

type Lexer struct {
	input        []byte
	position     int
	readPosition int
	ch           rune
}

func NewLexer(input []byte) *Lexer {
	l := &Lexer{input: input}
	l.readChar()
	return l
}

func (l *Lexer) readChar() {
	r, size := utf8.DecodeRune(l.input[l.readPosition:])
	if size == 0 {
		l.ch = 0
		l.position = l.readPosition
	} else {
		l.ch = r
		l.position = l.readPosition
		l.readPosition += size
	}
}

func (l *Lexer) NextToken() token.Token {
	var tok token.Token

	l.skipWhitespace()

	switch l.ch {
	case '(', '（':
		tok = newToken(token.LPAREN, '(')
	case ')', '）':
		tok = newToken(token.RPAREN, ')')
	case '+', '＋':
		tok = newToken(token.PLUS, '+')
	case '-', '－':
		tok = newToken(token.MINUS, '-')
	case '*', '＊':
		tok = newToken(token.ASTERISK, '*')
	case '/', '／':
		tok = newToken(token.SLASH, '/')
	case 0:
		tok.Literal = ""
		tok.Type = token.EOF
	default:
		if isDigit(l.ch) {
			tok.Literal = l.readNumber()
			tok.Type = token.INTEGER
			return tok
		} else {
			tok = newToken(token.ILLEGAL, l.ch)
		}
	}
	l.readChar()
	return tok
}

func newToken(tokenType token.TokenType, ch rune) token.Token {
	return token.Token{Type: tokenType, Literal: string(ch)}
}

func (l *Lexer) readNumber() string {
	var buff strings.Builder
	for isDigit(l.ch) {
		if l.ch >= '０' && l.ch <= '９' {
			buff.WriteRune(l.ch - '０' + '0')
		} else {
			buff.WriteRune(l.ch)
		}
		l.readChar()
	}
	return buff.String()
}

func isDigit(ch rune) bool {
	return unicode.IsDigit(ch)
}

func (l *Lexer) skipWhitespace() {
	for unicode.IsSpace(l.ch) {
		l.readChar()
	}
}
